#pragma once

namespace gecko
{

	namespace gtl
	{

		template<typename T, typename ALLOCATOR>
		Vector<T, ALLOCATOR>::Vector()
			: m_size(0)
			, m_capacity(0)
			, m_buffer(NULL)
		{
		}

		template<typename T, typename ALLOCATOR>
		Vector<T, ALLOCATOR>::~Vector()
		{
			Clear();
		}

		template<typename T, typename ALLOCATOR>
		void Vector<T, ALLOCATOR>::Reserve(size_t size)
		{
			if (size > m_capacity)
			{
				// allocate more memory
				T* newBuffer = ALLOCATOR::Allocate(sizeof(T) * size);

				// call constructor on new objects
				ALLOCATOR::Construct(newBuffer, size);

				if (m_buffer)
				{
					// copy existing buffer to new on
					ALLOCATOR::Copy(newBuffer, m_buffer, sizeof(T) * m_capacity);

					// call destructor on old objects
					ALLOCATOR::Destruct(m_buffer, m_capacity);

					// free old buffer
					ALLOCATOR::Free(m_buffer);
				}

				// point to new buffer
				m_buffer = newBuffer;

				// increase capacity
				m_capacity = size;
			}
		}

		template<typename T, typename ALLOCATOR>
		void Vector<T, ALLOCATOR>::PushBack( const T& item )
		{
			Reserve(m_size + 1);
			m_buffer[m_size++] = item;
		}

		template<typename T, typename ALLOCATOR>
		const T& Vector<T, ALLOCATOR>::PopBack() const
		{
			return m_buffer[--m_size];
		}

		template<typename T, typename ALLOCATOR>
		T& Vector<T, ALLOCATOR>::PopBack()
		{
			return m_buffer[--m_size];
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::Iterator Vector<T, ALLOCATOR>::Begin()
		{
			return Iterator(m_buffer);
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::Iterator Vector<T, ALLOCATOR>::End()
		{
			return Iterator(m_buffer + m_size);
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::ConstIterator Vector<T, ALLOCATOR>::Begin() const
		{
			return ConstIterator(m_buffer);
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::ConstIterator Vector<T, ALLOCATOR>::End() const
		{
			return ConstIterator(m_buffer + m_size);
		}

		template<typename T, typename ALLOCATOR>
		void Vector<T, ALLOCATOR>::Clear()
		{
			if (m_buffer)
			{
				// call destructor on new objects
				ALLOCATOR::Destruct(m_buffer, m_size);

				// free the buffer
				ALLOCATOR::Free(m_buffer);
				m_buffer = NULL;
			}

			m_size = 0;
			m_capacity = 0;
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::ConstIterator Vector<T, ALLOCATOR>::Find( const T& data ) const
		{
			for ( ConstIterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T, typename ALLOCATOR>
		typename Vector<T, ALLOCATOR>::Iterator Vector<T, ALLOCATOR>::Find( const T& data )
		{
			for ( Iterator itr = Begin(); itr != End(); ++itr )
			{
				if ( (*itr) == data )
				{
					return itr;
				}
			}

			return End();
		}

		template<typename T, typename ALLOCATOR>
		unsigned int gecko::gtl::Vector<T, ALLOCATOR>::Size()
		{
			return m_size;
		}

		template<typename T, typename ALLOCATOR>
		bool gecko::gtl::Vector<T, ALLOCATOR>::Empty()
		{
			return m_size == 0;
		}

		template<typename T, typename ALLOCATOR>
		T& gecko::gtl::Vector<T, ALLOCATOR>::Front()
		{
			return m_buffer[0];
		}

		template<typename T, typename ALLOCATOR>
		const T& gecko::gtl::Vector<T, ALLOCATOR>::Front() const
		{
			return m_buffer[0];
		}

		template<typename T, typename ALLOCATOR>
		T& gecko::gtl::Vector<T, ALLOCATOR>::Back()
		{
			return m_buffer[m_size - 1];
		}

		template<typename T, typename ALLOCATOR>
		const T& gecko::gtl::Vector<T, ALLOCATOR>::Back() const
		{
			return m_buffer[m_size - 1];
		}



		namespace internal
		{

			// iterator
			template<typename T>
			class VectorIterator
			{
			public:
				// construct iterator
				VectorIterator(T* current);

				// increment		
				VectorIterator& operator++();

				// decrement
				VectorIterator& operator--();

				// post-increment.
				VectorIterator& operator++(int);

				// post-decrement
				VectorIterator& operator--(int);

				// dereference
				T& operator*();

				// inequality
				bool operator!=(const VectorIterator& rhs) const;

				// equality
				bool operator==(const VectorIterator& rhs) const;

			private:
				// pointer to current item in vector buffer
				T* m_current;
			};

			template<typename T>
			VectorIterator<T>::VectorIterator( T* current ) 
				: m_current(current)
			{

			}

			template<typename T>
			VectorIterator<T>& VectorIterator<T>::operator++()
			{
				m_current++;
				return *this;
			}

			template<typename T>
			VectorIterator<T>& VectorIterator<T>::operator--()
			{
				m_current--;
				return *this;
			}

			template<typename T>
			VectorIterator<T>& VectorIterator<T>::operator++(int)
			{
				m_current++;
				return *this;
			}

			template<typename T>
			VectorIterator<T>& VectorIterator<T>::operator--(int)
			{
				m_current--;
				return *this;
			}

			template<typename T>
			T& VectorIterator<T>::operator*()
			{
				return *m_current;
			}

			template<typename T>
			bool VectorIterator<T>::operator!=(const VectorIterator& rhs) const
			{
				return m_current != rhs.m_current;
			}

			template<typename T>
			bool VectorIterator<T>::operator==(const VectorIterator& rhs) const
			{
				return m_current == rhs.m_current;
			}


			// simple allocator which just cals malloc() and free() for now
			template<typename T>
			class DefaultVectorBlockAllocator
			{
			public:
				// allocate a block of memory
				static T* Allocate(size_t size);

				// copy memory from src to dest
				static void Copy(T* dest, T* src, size_t size);

				// free off memory
				static void Free(T* mem);

				// call constructors on objects in contiguous memory
				static void Construct(T* mem, unsigned int count);

				// call destructors on objects in contiguous memory
				static void Destruct(T* mem, unsigned int count);
			};

			template<typename T>
			T* DefaultVectorBlockAllocator<T>::Allocate(size_t size)
			{
				return (T*)malloc(size);
			}

			template<typename T>
			void DefaultVectorBlockAllocator<T>::Copy(T* dest, T* src, size_t size)
			{
				memcpy(dest, src, size);
			}

			template<typename T>
			void DefaultVectorBlockAllocator<T>::Free(T* mem)
			{
				free(mem);
			}

			template<typename T>
			void DefaultVectorBlockAllocator<T>::Construct(T* mem, unsigned int count)
			{
				for (unsigned int i = 0; i < count; i++)
				{
					new (mem) T();
				}
			}

			template<typename T>
			void DefaultVectorBlockAllocator<T>::Destruct(T* mem, unsigned int count)
			{
				for (unsigned int i = 0; i < count; i++)
				{
					mem->~T();
				}
			}

		} // namespace internal

	} // namespace gtl

} // namespace gecko
